<template>
  <div class="commponent-card">
    <div class="display-area">
      <div ref="historyRef" class="history">
        <div class="line">
          <div class="prompt-line">
            <span class="input-text">$ 你好</span>
          </div>
        </div>
        <div v-for="(entry, index) in state.history" :key="index" class="line">
          <template v-if="entry.type === 'input'">
            <div class="prompt-line">
              <span class="prompt">{{ entry?.prompt || '$ ' }}</span>
              <span class="input-text">{{ entry?.text }}</span>
            </div>
          </template>
          <template v-else-if="entry.type === 'output'">
            <pre v-if="entry.dataType === 'ascii-image'" class="output-ascii-image">{{ entry.text }}</pre>
            <div v-else-if="entry.dataType === 'image'" class="output-image">
              <img :src="entry.text" />
            </div>
            <pre v-else class="output-text">{{ entry.text }}</pre>
          </template>
        </div>
        <div v-if="state.loading" class="line current loading">
          <span class="prompt">{{ state.loadingCharacter }}</span>
        </div>
      </div>
    </div>
    <div class="input-area">
      <input ref="inputRef" v-model="state.value" class="input" spellcheck="false" :placeholder="state.placeholder"
        @compositionstart="state.isComposing = true" @compositionend="state.isComposing = false" />
      <button type="button" @click="chat">AI Chat</button>
    </div>
    <div class="operations">
      <button type="button" @click="printer.clear()">清空</button>
      <button type="button" @click="sendPing">向平台发送ping</button>
      <button type="button" @click="getAgentList">agent list</button>
      <button type="button" @click="agentShow">agent show</button>
      <button type="button" @click="showAgentData">show</button>
    </div>
  </div>
</template>
<script setup>
import { onMounted, onBeforeUnmount, reactive, ref } from 'vue';
import aiw from '@/aiw-agent';

const state = reactive({
  agentId: null,
  value: '',
  placeholder: '请输入问题，开始聊天',
  isComposing: false,
  history: [],
  loading: false,
  loadingCharacter: '',
  streaming: false,
  msg: '你好，主页面！',
})

// 监听容器内部的变化，并及时滚动到底部
let observer = null;
const historyRef = ref(null);
// 开启监听
const observeAndScroll = () => {
  if (!historyRef.value) return;
  if (observer) return;

  observer = new MutationObserver(() => {
    requestAnimationFrame(() => {
      historyRef.value.scrollTop = historyRef.value.scrollHeight;
    });
  });

  observer.observe(historyRef.value, { childList: true, subtree: true });
};

const frames = ['⠋', '⠙', '⠹', '⠸', '⠼', '⠴', '⠦', '⠧', '⠇', '⠏'];
let i = 0;
let loadingInterval = null;

const printer = {
  print: async (text, type = 'output', dataType = 'text') => {
    if (dataType === 'image') {
      state.history.push({ type: type, text: text, dataType: 'image' });
    } else {
      state.history.push({ type: type, text: text });
    }
  },
  stream: (text) => {
    if (typeof text === 'boolean' && !text) {
      state.streaming = false;
      return;
    }
    if (state.streaming) {
      state.history[state.history.length - 1].text += text;
    } else {
      state.history.push({ type: 'output', text: text });
      state.streaming = true;
    }
  },
  clear: () => {
    state.history.length = 0;
    state.loading = false;
  },
  startLoading(text = '正在处理') {
    state.loading = true;
    if (loadingInterval) return;

    loadingInterval = setInterval(() => {
      const frame = frames[i = ++i % frames.length];
      state.loadingCharacter = frame;
    }, 100);
  },
  stopLoading() {
    clearInterval(loadingInterval);
    loadingInterval = null;
    state.loading = false;
  },
  scrollToBottom() {
    if (historyRef.value) {
      historyRef.value.scrollTop = historyRef.value.scrollHeight;
    }
  }
}

const sendPing = () => {
  console.log('[Agent] send ping');
  aiw.bridge.send('ping', { msg: state.msg },);
}
const getAgentList = () => {
  aiw.bridge.send('agent.list', { long: true },)
}
const agentShow = () => {
  aiw.bridge.send('agent.show', { current: true }, { source: 'console' })
}
const showAgentData = () => {
  const data = aiw.agent.get();
  printer.print(`${data?.name} | ${data?.cid}`, 'output');
}
const chat = () => {
  const input = state.value.trim();
  if (!input) return;
  state.history.push({ type: 'input', text: input });
  state.value = '';
  aiw.bridge.send('agent.chat', { prompt: input }, { stream: true })
  printer.startLoading();
}

const onPong = ({ status, data, message }, isStream, id, source) => {
  console.log('[Agent] 收到平台ping：', status, data, message, isStream, id, source)
  if (status === 'SUCCEEDED') {
    printer.print(data, 'output');
  } else {
    printer.print(message, 'output');
  }
}
onMounted(() => {
  observeAndScroll();
  console.log('console mounted');
  aiw.bridge.send('agentReady');
  aiw.bridge.on('ping', onPong);
  aiw.bridge.receive('ping', (args, ctx) => {
    console.log('[自定义 ping 响应]', args);
    ctx.reply({ status: 'OK', message: '我是自定义响应！' });
    printer.print(args?.message, 'output');
  });
  aiw.bridge.on('agent.list', (data, isStream, id, source) => {
    console.log('[Agent] agent list:', data, isStream, id, source)
    const list = data?.data || [];
    const text = list.map((item) => `${item?.name} | ${item?.cid}`).join('\n');
    printer.print(text, 'output');
  })
  aiw.bridge.on('agent.show', ({ status, data, message }, isStream, id, source) => {
    if (source !== 'console') return;
    if (status === 'SUCCEEDED') {
      const text = `${data?.name} | ${data?.cid}`;
      printer.print(text, 'output');
    } else if (message) {
      printer.print(message, 'output');
    }
  });
  aiw.bridge.on('agent.chat', ({ status, data, message }, isStream, id, source) => {
    // console.log('agent chat:', status, data, message, isStream, id, source)
    if (status === 'START') {
      printer.startLoading();
    }
    if (status === 'ERROR') {
      if (message && typeof message === 'string') {
        printer.print('😭 ' + message);
        printer.stopLoading();
        printer.stream(false);
      }
    } else if (isStream && data && typeof data === 'string') {
      printer.stream(data);
    } else if (status === 'MEME_START') {
      printer.stream(false);
    } else if (status === 'MEME_END') {
      printer.print(data, 'output', 'image');
    }
    if (status === '[END]') {
      printer.stopLoading();
      printer.stream(false);
    }
  })
})
onBeforeUnmount(() => {
  aiw.bridge.off('ping', onPong);
  if (observer) {
    observer.disconnect(); // 取消监听
    observer = null;
  }
});
</script>

<style lang="scss" scoped>
.display-area {
  padding: 16px;
  border-radius: 8px;
  border: 1px solid rgba(128, 128, 128, 0.2);
  background-color: rgba(128, 128, 128, 0.06);

  .history {
    height: 400px;
    overflow-x: hidden;
    overflow-y: auto;
  }

  .line {
    font-size: 14px;
  }
}

.input-area {
  display: flex;
  align-items: center;
  gap: 6px;
  margin: 8px 0;

  .input {
    flex: 1;
    padding: 8px 16px;
    border-radius: 4px;
    border: 1px solid rgba(128, 128, 128, 0.2);
    box-sizing: border-box;
  }
}

.operations {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
}
</style>
